Compare commits

...

17 Commits

Author SHA1 Message Date
Felipe 94d51dac79 Update layout.html 2013-09-17 16:13:49 +02:00
Felipe d4edfdb2bc Update about.html 2013-09-17 16:13:10 +02:00
Felipe dc54252660 Updated deploy script to match github repository 2013-09-17 15:46:31 +02:00
Felipe b93544fc58 Updated base requirements (django security update) 2013-09-16 13:16:57 +02:00
Felipe Martín 7ad3c3e675 Added Achievements to the public 2013-06-03 18:04:12 +02:00
Felipe Martín 41e6549742 [utils.miner] Added -sort of- mob miner 2013-06-03 17:42:45 +02:00
Felipe Martín aba1fa1dc0 [utils.miner] Typo: fetched -> found 2013-06-03 16:57:20 +02:00
Felipe Martín ffd32c9fc8 [utils.miner] configuration SAVE defaults to False 2013-06-03 16:57:09 +02:00
Felipe Martín cb025ee214 [utils.miner] Added achievement mining 2013-06-03 16:51:36 +02:00
Felipe Martín d89bd44f8b [utils.miner] Improved output with colouring in python scripts 2013-06-03 16:51:14 +02:00
Felipe Martín 676cd2bf65 [utils.miner] Added coloring utils 2013-06-03 16:50:38 +02:00
Felipe Martín 68d1611c60 Added database.achievements 2013-06-03 16:50:02 +02:00
Felipe Martín 463b7f4a83 Added possibility to show the version list as blocks instead of a list (same as items/blocks). Only internal, no public interface provided. 2013-05-30 18:49:47 +02:00
Felipe Martín a449af2fdc Some improvements:
+ Version list reverse name is now "versions_list" and all sites are using it
+ Blog pages now have a title
2013-05-30 18:34:17 +02:00
Felipe Martín 0f73dcb540 Added more intensity to the blog renderer for dark textures 2013-05-30 11:50:34 +02:00
Felipe Martín 503a0f2cd6 Told DDT to now intercept redirect on development 2013-05-30 11:50:12 +02:00
Felipe Martín 718c3729f6 Added blocks header and footer to the main layout, also container div is now inside the content block, so it's optional 2013-05-30 11:49:51 +02:00
30 changed files with 838 additions and 199 deletions

View File

@ -25,3 +25,7 @@ INSTALLED_APPS = INSTALLED_APPS + ('debug_toolbar', )
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + ('debug_toolbar.middleware.DebugToolbarMiddleware',)
INTERNAL_IPS = ('127.0.0.1',)
DEBUG_TOOLBAR_CONFIG = {
'INTERCEPT_REDIRECTS': False
}

View File

@ -7,7 +7,7 @@ from shutil import copytree
# Config
APP_DIRECTORY = './app'
GIT_REPOSITORY = 'git@bitbucket.org:fmartingr/minecraftcodex.git'
GIT_REPOSITORY = 'git@github.com:fmartingr/minecraftcodex.git'
GIT_DOWNLOAD_DIR = './src'
GIT_APP_PATH = 'minecraftcodex'
GIT_PATH_THIS = '%s/config/production/deploy.py' % GIT_DOWNLOAD_DIR

View File

@ -1,5 +1,5 @@
Django==1.5.1
Django==1.5.4
jingo==0.6.1
django-grappelli==2.4.4
South==0.7.6
pytz==2013b
django-grappelli==2.4.6
South==0.8.2
pytz==2013d

View File

@ -1,6 +1,11 @@
{% extends "layout.html" %}
{% block head_title %}
{{ super() }} | Blog
{% endblock %}
{% block content %}
<div class="container">
<h1>Blog</h1>
{% for item in page.object_list %}
<hr />
@ -30,4 +35,5 @@
</ul>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,6 +1,11 @@
{% extends "layout.html" %}
{% block head_title %}
{{ super() }} | Blog | {{ item.title }}
{% endblock %}
{% block content %}
<div class="container">
<p><a href="{{ url('blog_list') }}"><i class="icon-arrow-left"></i> Go back</a></p>
<article class="blog-entry">
<h1>{{ item.title }}</h1>
@ -13,4 +18,5 @@
{{ item.content|safe }}
</div>
</article>
</div>
{% endblock %}

View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Achievement'
db.create_table(u'database_achievement', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('internal_name', self.gf('django.db.models.fields.CharField')(max_length=128)),
('internal_id', self.gf('django.db.models.fields.IntegerField')(default=0)),
))
db.send_create_signal(u'database', ['Achievement'])
def backwards(self, orm):
# Deleting model 'Achievement'
db.delete_table(u'database_achievement')
models = {
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'database.achievement': {
'Meta': {'object_name': 'Achievement'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'internal_id': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'internal_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
u'database.block': {
'Meta': {'object_name': 'Block'},
'data_value': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'internal_id': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'internal_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'main_texture': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['database.Texture']", 'null': 'True', 'blank': 'True'}),
'version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['database.Version']", 'null': 'True', 'blank': 'True'})
},
u'database.item': {
'Meta': {'object_name': 'Item'},
'data_value': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'internal_id': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'internal_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'main_texture': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['database.Texture']", 'null': 'True', 'blank': 'True'}),
'version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['database.Version']", 'null': 'True', 'blank': 'True'})
},
'database.jarfile': {
'Meta': {'object_name': 'JarFile'},
'description': ('django.db.models.fields.CharField', [], {'default': "'client'", 'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['database.Version']"})
},
u'database.language': {
'Meta': {'object_name': 'Language'},
'code': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
'region': ('django.db.models.fields.CharField', [], {'max_length': '32'})
},
u'database.languagestring': {
'Meta': {'object_name': 'LanguageString'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'language': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['database.Language']"}),
'value': ('django.db.models.fields.TextField', [], {})
},
'database.mod': {
'Meta': {'ordering': "['name']", 'object_name': 'Mod'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
},
u'database.modelattribute': {
'Meta': {'object_name': 'ModelAttribute'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'value': ('django.db.models.fields.TextField', [], {})
},
u'database.texture': {
'Meta': {'object_name': 'Texture'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'image': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'items'", 'max_length': '16'})
},
'database.version': {
'Meta': {'ordering': "['date']", 'object_name': 'Version'},
'changelog': ('django.db.models.fields.TextField', [], {}),
'date': ('django.db.models.fields.DateField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mod': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['database.Mod']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
'snapshot': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'release'", 'max_length': '10', 'blank': 'True'}),
'upcoming': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'version_number': ('django.db.models.fields.CharField', [], {'default': "'0.1.0'", 'max_length': '256'})
}
}
complete_apps = ['database']

View File

@ -379,3 +379,41 @@ class LanguageStringAdmin(admin.ModelAdmin):
search_fields = ('key', 'value', )
admin.site.register(LanguageString, LanguageStringAdmin)
###
# ACHIEVEMENTS
###
class Achievement(models.Model):
internal_name = models.CharField(max_length=128)
internal_id = models.IntegerField(default=0)
def name(self):
result = self.internal_name
try:
string = LanguageString.objects.get(
language=14,
key='achievement.%s' % self.internal_name
)
result = string.value
except:
pass
return result
def description(self):
result = self.internal_name
try:
string = LanguageString.objects.get(
language=14,
key='achievement.%s.desc' % self.internal_name
)
result = string.value
except:
pass
return result
class AchievementAdmin(admin.ModelAdmin):
list_display = ('name', 'description', )
search_fields = ('internalName', 'description', )
admin.site.register(Achievement, AchievementAdmin)

View File

@ -92,6 +92,7 @@ window.BlockRenderer = (model, textures, dom, width, height) ->
@prepareLight = ->
light = new THREE.DirectionalLight 0xffffff
light.position.set(1, 20, 60).normalize()
light.intensity = 1.6
_scene.add light

View File

@ -5,70 +5,67 @@
{% endblock %}
{% block content %}
<h1>About</h1>
<!-- PROJECT SECTION -->
<h2>The project</h1>
<img src="/static/images/about/lan.original.jpg" class="pull-right img-polaroid span4" />
<p>Ooh, name it after me! Alright, let's mafia things up a bit. Joey, burn down the ship. Clamps, burn down the crew. Pansy. Really?! Okay, it's 500 dollars, you have no choice of carrier, the battery can't hold the charge and the reception isn't very&hellip; Leela's gonna kill me.</p> <h2>A Pharaoh to Remember</h2> <p>I can explain. It's very valuable. Oh God, what have I done? Bender, you risked your life to save me! I don't know what you did, Fry, but once again, you screwed up! Now all the planets are gonna start cracking wise about our mamas. Why would a robot need to drink? Ah, yes! John Quincy Adding Machine. He struck a chord with the voters when he pledged not to go on a killing spree.</p> <ul> <li>Goodbye, friends. I never thought I'd die like this. But I always really hoped.</li> <li>Now Fry, it's been a few years since medical school, so remind me. Disemboweling in your species: fatal or non-fatal?</li> <li>Who said that? SURE you can die! You want to die?!</li> </ul> <h3>Time Keeps on Slippin'</h3> <p>There's no part of that sentence I didn't like! I guess because my parents keep telling me to be more ladylike. As though! Yeah, and if you were the pope they'd be all, "Straighten your pope hat." And "Put on your good vestments."</p> <h4>Where the Buggalo Roam</h4> <p>But I've never been to the moon! This is the worst part. The calm before the battle. But I've never been to the moon! Whoa a real live robot; or is that some kind of cheesy New Year's costume? You wouldn't. Ask anyway!</p>
<div class="clearfix"></div>
<div class="container">
<h1>About</h1>
<!-- PROJECT SECTION -->
<img src="/static/images/about/lan.original.jpg" class="pull-right img-polaroid span4" />
<p>
This is not intended to be an actual database site (there are already a few), this is a playground
for a tool I coded to extract information from the minecraft obfuscated code.
</p>
<p>
The tool itself is developed in python, and you can access its codebase
<a href="https://github.com/fmartingr/minecraft-jar-miner">at github</a>,
play with it and pull request if you want. Any improvement is appreciated.
</p>
<p>
Don't expect a full database in a short-mid term, since I'm really busy with my work and other projects I
have, but if you want to suggest something, feel free to contact me in the details you have on the footer
or via my personal twitter below. Thanks!
</p>
<div class="clearfix"></div>
<!-- PEOPLE SECTION -->
<h2>The people</h2>
<div class="row">
<div class="span6">
<div class="well">
<img src="https://www.gravatar.com/avatar/49dbfd4eff4d399330de2bc4b03cb334?s=200px"
class="img-circle pull-left" />
<div class="span3 pull-right">
<h3>Felipe Mart&iacute;n</h3>
<p>Developer and minecraft lover.</p>
<p>
Want to help the awesome minecraft community building a tool to
help everyone on its days over <i>minecraftia</i>.
<p>
<a href="https://twitter.com/fmartingr">
<i class="icon-twitter"></i> @fmartingr
</a>
</p>
</div>
<div class="clearfix"></div>
<!-- PEOPLE SECTION -->
<h2>Who's behind this?</h2>
<div class="row">
<div class="span6">
<div class="well">
<img src="https://www.gravatar.com/avatar/49dbfd4eff4d399330de2bc4b03cb334?s=200px"
class="img-circle pull-left" />
<div class="span3 pull-right">
<h3>Felipe Mart&iacute;n</h3>
<p>Developer and minecraft lover.</p>
<p>
Want to help the awesome minecraft community building a tool to
help everyone on its days over <i>minecraftia</i>.
<p>
<a href="https://twitter.com/fmartingr">
<i class="icon-twitter"></i> @fmartingr
</a>
</p>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="span6">
<div class="well">
<img src="https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&amp;s=200px"
class="img-circle pull-left" />
<div class="span3 pull-right">
<h3>Ramiro Sanchez</h3>
<p>Design and stuff.</p>
<p>I haven't done anything yet, but it's cool to be here!<br />:)</p>
<p>
<a href="https://twitter.com/rsanchezbalo">
<i class="icon-twitter"></i> @rsanchezbalo
</a>
</p>
</div>
<div class="clearfix"></div>
{# TEMPLATE
<div class="span6">
<div class="well">
<img src="avatar"
class="img-circle pull-left" />
<div class="span3 pull-right">
<h3>Name</h3>
<p>What it does</p>
<p>Little text</p>
<p>
<a href="https://twitter.com/">
<i class="icon-twitter"></i> @
</a>
</p>
</div>
<div class="clearfix"></div>
</div>
</div>
#}
</div>
{# TEMPLATE
<div class="span6">
<div class="well">
<img src="avatar"
class="img-circle pull-left" />
<div class="span3 pull-right">
<h3>Name</h3>
<p>What it does</p>
<p>Little text</p>
<p>
<a href="https://twitter.com/">
<i class="icon-twitter"></i> @
</a>
</p>
</div>
<div class="clearfix"></div>
</div>
</div>
#}
</div>
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends "layout.html" %}
{% block page_title %}
{{ super() }} | Achievements
{% endblock %}
{% block content %}
<div class="container">
<h1>Achievements</h1>
<hr />
<div class="row-fluid">
{% for item in page.object_list %}
<div class="span6">
<!--<a href="#">-->
<div class="well">
<h4 class="text-black">{{ item.name() }}</h4>
<div class="text-black">{{ item.description() }}</div>
</div>
<!--</a>-->
</div>
{% if loop.index % 2 == 0 %}
</div>
<div class="row-fluid">
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -1,6 +1,7 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
<h1>Blocks</h1>
<hr />
<div class="row-fluid">
@ -70,4 +71,5 @@
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@ -9,33 +9,35 @@
{% endblock %}
{% block content %}
<div class="row-fluid">
<div class="span9">
<h1>{{ item.name() }}</h1>
<hr />
<h3>Can drop</h3>
...
<h3>Used in</h3>
...
</div>
<div class="span3">
<div class="well">
<div class="block-render">
<div class="codexicon x8 margin-auto text-center">
{% if item.main_texture %}
<img src="/static/textures/{{ item.main_texture.get_image(8) }}" />
{% endif %}
<div class="container">
<div class="row-fluid">
<div class="span9">
<h1>{{ item.name() }}</h1>
<hr />
<h3>Can drop</h3>
...
<h3>Used in</h3>
...
</div>
<div class="span3">
<div class="well">
<div class="block-render">
<div class="codexicon x8 margin-auto text-center">
{% if item.main_texture %}
<img src="/static/textures/{{ item.main_texture.get_image(8) }}" />
{% endif %}
</div>
</div>
{% if user.is_authenticated() %}
<script defer="defer" type="text/javascript">
block = BlockRenderer('block', {side:'/static/textures/{{ item.main_texture.get_image() }}'}, '.block-render', 'auto', 'auto');
</script>
{% endif %}
<ul>
<li>Data value: {{ item.data_value }}</li>
</li>
<div class="clearfix"></div>
</div>
{% if user.is_authenticated() %}
<script defer="defer" type="text/javascript">
block = BlockRenderer('block', {side:'/static/textures/{{ item.main_texture.get_image() }}'}, '.block-render', 'auto', 'auto');
</script>
{% endif %}
<ul>
<li>Data value: {{ item.data_value }}</li>
</li>
<div class="clearfix"></div>
</div>
</div>
</div>

View File

@ -1,6 +1,7 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
<h1>Items</h1>
<hr />
<div class="row-fluid">
@ -70,4 +71,5 @@
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@ -1,26 +1,28 @@
{% extends "layout.html" %}
{% block content %}
<div class="row-fluid">
<div class="span9">
<h1>{{ item.name() }}</h1>
<hr />
<h3>Can drop</h3>
...
<h3>Used in</h3>
...
</div>
<div class="span3">
<div class="well">
<div class="codexicon x8 margin-auto text-center">
{% if item.main_texture %}
<img src="/static/textures/{{ item.main_texture.get_image(8) }}" />
{% endif %}
<div class="container">
<div class="row-fluid">
<div class="span9">
<h1>{{ item.name() }}</h1>
<hr />
<h3>Can drop</h3>
...
<h3>Used in</h3>
...
</div>
<div class="span3">
<div class="well">
<div class="codexicon x8 margin-auto text-center">
{% if item.main_texture %}
<img src="/static/textures/{{ item.main_texture.get_image(8) }}" />
{% endif %}
</div>
<ul>
<li>Data value: {{ item.data_value }}</li>
</li>
<div class="clearfix"></div>
</div>
<ul>
<li>Data value: {{ item.data_value }}</li>
</li>
<div class="clearfix"></div>
</div>
</div>
</div>

View File

@ -31,11 +31,10 @@ Thank you!
<title>{% block head_title %}{{ site_title }}{% endblock %}</title>
</head>
<body>
{% block header %}
<div class="alert alert-info margin-none text-center">
<strong>
Lack of information?
We are in <u>alpha</u>!
Expect more data over the next weeks.
Not a complete databae? I know! Read the about section for more info.
</strong>
</div>
<header>
@ -64,8 +63,11 @@ Thank you!
<li class="{% if section == 'items' %}active{% endif %}">
<a href="{{ url('items_list') }}">Items</a>
</li>
<li class="{% if section == 'achievements' %}active{% endif %}">
<a href="{{ url('achievements') }}">Achievements</a>
</li>
<li class="{% if section == 'versions' %}active{% endif %}">
<a href="{{ url('version_list') }}">Versions</a>
<a href="{{ url('versions_list') }}">Versions</a>
</li>
</ul>
<ul class="nav pull-right">
@ -81,10 +83,11 @@ Thank you!
</div><!-- /navbar-inner -->
</div>
</header>
<div class="container">
{% block content %}
{% endblock %}
</div>
{% endblock %}
{% block content %}
<div class="container"></div>
{% endblock %}
{% block footer %}
<footer class="container-fluid">
<hr style="margin: 0" />
<div class="pull-left">
@ -103,16 +106,17 @@ Thank you!
</a>
</div>
</footer>
{% if google_analytics %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
{% if google_analytics and not user.is_authenticated() %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ google_analytics }}', 'minecraftcodex.com');
ga('send', 'pageview');
</script>
{% endif %}
ga('create', '{{ google_analytics }}', 'minecraftcodex.com');
ga('send', 'pageview');
</script>
{% endif %}
{% endblock %}
</body>
</html>

View File

@ -5,9 +5,10 @@
{% endblock %}
{% block content %}
<div class="container">
<h1>Minecraft versions</h1>
<hr />
<a href="/versions/"><i class="icon-arrow-left"></i> Go back</a>
<a href="{{ url('versions_list') }}"><i class="icon-arrow-left"></i> Go back</a>
<h2>{% if status != 'release' %}{{ status }}{% endif %} {{ version_number }}</h2>
{% if results > 1 %}
<div class="alert alert-info">This version contains more than one release.</div>
@ -24,4 +25,5 @@
<h3>Changelog</h3>
<p>{{ version.changelog|nl2br }}</p>
{% endfor %}
</div>
{% endblock %}

View File

@ -5,8 +5,41 @@
{% endblock %}
{% block content %}
<div class="container">
<h1>Minecraft versions</h1>
<hr />
{% if show == 'squares' %}
<div class="row-fluid">
{% for item in page.object_list %}
<div class="span3">
<a href="{{ url('version', item.status, item.version_number) }}">
<div class="well">
<div class="row-fluid">
<div class="span6">
<span class="label {% if item.status == 'release' %}label-info{% endif %}">{{ item.version_number }}</span>
{% if item.jarfiles %}
&nbsp;<span class="label label-warning"
title="Downloads available">D</span>
{% endif %}
</div>
<div class="span6">
<p class="text-black text-right">{{ item.date }}</p>
</div>
</div>
<!--<p class="text-black text-center">{{ item.name }}</p>-->
<div class="text-right">
More details &raquo;
</div>
</div>
</a>
</div>
{% if loop.index % 4 == 0 %}
</div>
<div class="row-fluid">
{% endif %}
{% endfor %}
</div>
{% else %}
<table class="table table-condensed table-hover">
<thead>
<tr>
@ -49,13 +82,15 @@
{% endfor %}
</tbody>
</table>
{% endif %}
<div class="pagination pagination-centered">
<ul>
{% for p in range(1, paginator.num_pages+1) %}
<li {% if p == page_number %}class="active"{% endif %}>
<a href="{{ url('version_list') }}?page={{ p }}">{{ p }}</a>
<a href="{{ url('versions_list') }}?page={{ p }}{% if show != 'list' %}&amp;show={{ show }}{% endif %}">{{ p }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
from django.shortcuts import render_to_response
from database.models import Version, Item, Block
from database.models import Version, Item, Block, Achievement
from django.core.paginator import Paginator
from django.template import RequestContext
from django.http import HttpResponseForbidden, Http404
@ -12,17 +12,23 @@ def home(request):
def versions(request):
section = 'versions'
show_options = ['list', 'squares']
show = 'list'
versions = Version.objects.filter(snapshot=False).\
order_by('-date', '-version_number')
paginator = Paginator(versions, 50)
paginator = Paginator(versions, 48)
page_number = 1
if 'page' in request.GET:
page_number = int(request.GET['page'])
if 'show' in request.GET:
if request.GET['show'] in show_options:
show = request.GET['show']
page = paginator.page(page_number)
data = {
'show': show,
'section': section,
'page': page,
'page_number': page_number,
@ -119,6 +125,28 @@ def blocks_detail(request, data_value):
else:
raise Http404
def achievements(request):
section = 'achievements'
items = Achievement.objects.all()
paginator = Paginator(items, 48)
page_number = 1
if 'page' in request.GET:
page_number = int(request.GET['page'])
page = paginator.page(page_number)
data = {
'section': section,
'page': page,
'page_number': page_number,
'paginator': paginator,
}
context = RequestContext(request, data)
return render_to_response('achievements.html', context_instance=context)
def about(request):
context = RequestContext(request, {'section': 'about'})
return render_to_response('about.html', context_instance=context)

View File

@ -34,16 +34,19 @@ urlpatterns = patterns('',
'database.views.version',
name='version'
),
url(r'^versions/', 'database.views.versions', name='version_list'),
url(r'^versions/', 'database.views.versions', name='versions_list'),
# Items
url(r'^items/(?P<data_value>\d+)/', 'database.views.items_detail', name='items_detail'),
url(r'^items/', 'database.views.items', name='items_list'),
# Items
# Blocks
url(r'^blocks/(?P<data_value>\d+)/', 'database.views.blocks_detail', name='blocks_detail'),
url(r'^blocks/', 'database.views.blocks', name='blocks_list'),
# Achievements
url(r'^achievements/$', 'database.views.achievements', name='achievements'),
# Blog
url(r'^blog/$', 'blog.views.blog', name='blog_list'),
url(r'^blog/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)/(?P<slug>[\w\-]+)/$',

124
utils/miner/achievements.py Normal file
View File

@ -0,0 +1,124 @@
#!/usr/bin/env python
# General libs
import re
import json
import os
import sys
# Tool libs
from utils import run, sanitize
import utils
import conf
from objects import GameAchievement
utils.title('ACHIEVEMENTS')
if conf.SAVE:
sys.path.append('../../minecraftcodex')
os.environ['DJANGO_SETTINGS_MODULE'] = 'local_settings'
from database.models import Achievement
###
# GLOBALS
###
ITEMS = []
###
# LOOK FOR CORRECT JAVA FILES
###
utils.sub("Looking for java files...")
#utils.sub("Keywords: %s" % ', '.join(conf.ACHIEVEMENTS_JAVA_KEYWORDS))
for keyword in conf.ACHIEVEMENTS_JAVA_KEYWORDS:
cmd = run('grep \'%s\' ./classes/*' % keyword)
for result in cmd:
if result and result is not '':
java_file = os.path.basename(result.strip().split()[0][:-1])
if java_file not in conf.ACHIEVEMENTS_FILES:
utils.echo("%s " % java_file, end='')
conf.ACHIEVEMENTS_FILES.append(java_file)
utils.echo('\r')
###
# GET ITEMS INFO FROM CLASSFILE
###
utils.sub('Looking for dataz', end='\n')
# Old items for final count
try:
OLD_ITEMS = json.loads(open('achievements.json').read())
except:
OLD_ITEMS = {}
OLD_ITEMS['list'] = []
for java_file in conf.ACHIEVEMENTS_FILES:
file_handler = open('./classes/%s' % java_file)
data = file_handler.read().split("\n")
item_regex = re.compile(conf.ACHIEVEMENTS_PATTERN)
class_error_regex = re.compile('name \'(?P<name>\w+)\' is not defined')
for line in data:
if '"' in line:
t = item_regex.search(line)
if t:
item = t.groupdict()
if conf.DEBUG:
print("Line: " + item['code'])
item['code'] = sanitize(item['code'])
if conf.DEBUG:
print("Sanitize: " + item['code'])
try:
obj = eval(item['code'])
except NameError as error:
# Create class for the given classname
class_name = class_error_regex.search(error.__str__()).group('name')
if conf.DEBUG:
print("Classname: %s" % class_name)
setattr(sys.modules[__name__], class_name, type(class_name, (GameAchievement,), {}))
obj = eval(item['code'])
#if obj.name == 'appleGold':
if conf.DEBUG:
print("result object: " + obj.__str__())
print('- - - - - -')
ITEMS.append(obj)
if conf.SAVE:
for item in ITEMS:
try:
obj = Achievement.objects.get(
internal_name=item.name,
internal_id=item.id
)
except Achievement.DoesNotExist:
obj = Achievement(
internal_name=item.name,
internal_id=item.id
)
obj.save()
# Print the miner summary and compile the new old data
new_old_data = {}
new_old_data['list'] = []
[new_old_data['list'].append(x.name) for x in ITEMS]
new_items = len(new_old_data['list'])-len(OLD_ITEMS['list'])
utils.info('Found %d achievements (%d new)' % (len(new_old_data['list']), new_items))
if new_items != 0:
utils.sub('Modifications:', end='\n')
for item in ITEMS:
if item.name not in OLD_ITEMS['list']:
utils.sub(' + %s' % item.name, end='\n', color=utils.colors.GREEN)
for item in OLD_ITEMS['list']:
if item not in new_old_data['list']:
utils.sub(' - %s' % item, end='\n', color=utils.colors.RED)
olditems = open('achievements.json', 'w')
olditems.write(json.dumps(new_old_data))

View File

@ -7,12 +7,12 @@ import os
import sys
# Tool libs
from utils import run, sanitize
import utils
import conf
from objects import GameBlock
print("=> Phase: blocks")
utils.title('BLOCKS')
if conf.SAVE:
sys.path.append('../../minecraftcodex')
os.environ['DJANGO_SETTINGS_MODULE'] = 'local_settings'
@ -26,10 +26,10 @@ BLOCKS = []
###
# LOOK FOR CORRECT JAVA FILES
###
print(" => Looking for java files...")
print(" Keywords: %s" % ', '.join(conf.BLOCKS_JAVA_KEYWORDS))
utils.sub("Looking for java files...")
#utils.sub("Keywords: %s" % ', '.join(conf.BLOCKS_JAVA_KEYWORDS), end='\n')
for keyword in conf.BLOCKS_JAVA_KEYWORDS:
command = run('grep \'%s\' ./classes/*' % keyword)
command = utils.run('grep \'%s\' ./classes/*' % keyword)
lines = []
[lines.append(x) for x in command]
lines = ''.join(lines).split('\n')
@ -37,13 +37,15 @@ for keyword in conf.BLOCKS_JAVA_KEYWORDS:
if result and result is not '':
java_file = os.path.basename(result.strip().split()[0][:-1])
if java_file not in conf.BLOCKS_FILES:
print(" Found: %s" % java_file)
utils.echo("%s " % java_file, end='')
conf.BLOCKS_FILES.append(java_file)
utils.echo('\r')
###
# GET ITEMS INFO FROM CLASSFILE
###
print(" => Mining blocks...")
utils.sub('Looking for dataz', end='\n')
# Old items for final count
try:
@ -67,7 +69,7 @@ for java_file in conf.BLOCKS_FILES:
if conf.DEBUG:
print("Line: " + item['code'])
item['code'] = sanitize(item['code'])
item['code'] = utils.sanitize(item['code'])
if conf.DEBUG:
print("Sanitize: " + item['code'])
@ -110,21 +112,20 @@ if conf.SAVE:
obj.save()
# Print the miner summary and compile the new old data
print(' => Summary')
new_old_data = {}
new_old_data['list'] = []
[new_old_data['list'].append(x.name) for x in BLOCKS]
new_blocks = len(new_old_data['list'])-len(OLD_BLOCKS['list'])
print(' Fetched %d blocks (%d new)' % (len(new_old_data['list']), new_blocks))
if new_blocks > 0:
print(' Modifications:')
utils.info('Found %d blocks (%d new)' % (len(new_old_data['list']), new_blocks))
if new_blocks != 0:
utils.sub('Modifications:', end='\n')
for item in BLOCKS:
if item.name not in OLD_BLOCKS['list']:
print(' + %s' % item.name)
utils.sub(' + %s' % item.name, end='\n', color=utils.colors.GREEN)
for item in OLD_BLOCKS['list']:
if item not in new_old_data['list']:
print(' - %s' % item)
utils.sub(' - %s' % item, end='\n', color=utils.colors.RED)
oldblocks = open('blocks.json', 'w')
oldblocks.write(json.dumps(new_old_data))

View File

@ -1,7 +1,7 @@
DEBUG = False
# Save to database?
SAVE = True
SAVE = False
###
# TEXTURES
@ -12,7 +12,6 @@ TEXTURES_EXTRA_SIZES_MULTIPLIER = [2, 4, 6, 8]
###
# ITEMS
###
ITEMS_FILES = []
ITEMS_JAVA_KEYWORDS = ['flintAndSteel', 'axeStone', 'swordDiamond']
ITEMS_PATTERN = "new (?P<code>[a-z]{2}\((?P<id>[1-9]{1,3}).*\"(?P<name>\w+)\"\))"
@ -20,7 +19,6 @@ ITEMS_PATTERN = "new (?P<code>[a-z]{2}\((?P<id>[1-9]{1,3}).*\"(?P<name>\w+)\"\))
###
# BLOCKS
###
BLOCKS_FILES = []
BLOCKS_JAVA_KEYWORDS = ['stonebrick']
BLOCKS_PATTERN = "new (?P<code>[a-z]{1,3}\((?P<id>[1-9]{1,3}).*\"(?P<name>\w+)\"\))"
@ -35,11 +33,23 @@ LANGUAGES_MASTER_KEYS = [
'language.code',
]
###
# ACHIEVEMENTS
###
ACHIEVEMENTS_FILES = []
ACHIEVEMENTS_JAVA_KEYWORDS = ['onARail', 'flyPig']
ACHIEVEMENTS_PATTERN = "new (?P<code>[a-z]{1,2}\((?P<id>[1-9]{1,3})\, \"(?P<name>\w+)\".*\))"
###
# MOBS
###
MOBS_FILES = []
MOBS_JAVA_KEYWORDS = ['mob/']
MOBS_PATTERN = '(?P<full>\"mob/(?P<name>[a-zA-Z0-9]+)\.png\")'
###
# BLACKLIST
###
CLASS_BLACKLIST = [
'and', 'abs', 'all', 'any', 'bin', 'chr'
]

View File

@ -4,7 +4,7 @@
JARFILE=$1
if [ "$JARFILE" != "" ]; then
echo "=> Phase: JAR file"
echo "[] Preparing files from JAR: $JARFILE []"
# Remove old
rm -rf ./jarfile ./classes
@ -13,18 +13,18 @@ if [ "$JARFILE" != "" ]; then
mkdir classes
# Decompress the jarfile into the jarfile folder
echo " => Unpackaging jar file"
echo " Unpackaging jar file"
unzip -qq $JARFILE -d ./jarfile
# Find all the classes and pass then through JAD
#find . -type d -name *.class -exec rm -rf {} \;
echo " => Decompiling classes"
echo " Decompiling classes"
ls ./jarfile/*.class | xargs -n1 ./tools/jad/jad -sjava -dclasses &> /dev/null
# Remove classfiles and left only other files
echo " => Cleaning"
echo " Cleaning"
rm ./jarfile/*.class
else
echo "No jarfile specified."
exit
echo "[!] No jarfile specified!"
exit -1
fi

View File

@ -7,12 +7,12 @@ import os
import sys
# Tool libs
from utils import run, sanitize
import utils
import conf
from objects import GameItem
print("=> Phase: items")
utils.title("ITEMS")
if conf.SAVE:
sys.path.append('../../minecraftcodex')
@ -27,21 +27,23 @@ ITEMS = []
###
# LOOK FOR CORRECT JAVA FILES
###
print(" => Looking for java files...")
print(" Keywords: %s" % ', '.join(conf.ITEMS_JAVA_KEYWORDS))
utils.sub("Looking for java files: ")
#utils.sub("Keywords: %s" % ', '.join(conf.ITEMS_JAVA_KEYWORDS), end='\n')
for keyword in conf.ITEMS_JAVA_KEYWORDS:
cmd = run('grep \'%s\' ./classes/*' % keyword)
cmd = utils.run('grep \'%s\' ./classes/*' % keyword)
for result in cmd:
if result and result is not '':
java_file = os.path.basename(result.strip().split()[0][:-1])
if java_file not in conf.ITEMS_FILES:
print(" Found: %s" % java_file)
utils.echo("%s " % java_file, end='')
conf.ITEMS_FILES.append(java_file)
utils.echo('\r')
###
# GET ITEMS INFO FROM CLASSFILE
###
print(" => Mining items...")
utils.sub('Looking for dataz', end='\n')
# Old items for final count
try:
@ -65,7 +67,7 @@ for java_file in conf.ITEMS_FILES:
if conf.DEBUG:
print("Line: " + item['code'])
item['code'] = sanitize(item['code'])
item['code'] = utils.sanitize(item['code'])
if conf.DEBUG:
print("Sanitize: " + item['code'])
@ -109,21 +111,20 @@ if conf.SAVE:
# Print the miner summary and compile the new old data
print(' => Summary')
new_old_data = {}
new_old_data['list'] = []
[new_old_data['list'].append(x.name) for x in ITEMS]
new_items = len(new_old_data['list'])-len(OLD_ITEMS['list'])
print(' Fetched %d items (%d new)' % (len(ITEMS), new_items))
if new_items > 0:
print(' Modifications:')
utils.info('Found %d items (%d new)' % (len(ITEMS), new_items))
if new_items != 0:
utils.sub('Modifications', end='\n')
for item in ITEMS:
if item.name not in OLD_ITEMS['list']:
print(' + %s' % item.name)
utils.sub(' + %s' % item.name, end='\n', color=utils.colors.GREEN)
for item in OLD_ITEMS['list']:
if item not in new_old_data['list']:
print(' - %s' % item)
utils.sub(' - %s' % item, end='\n', color=utils.colors.RED)
olditems = open('items.json', 'w')
olditems.write(json.dumps(new_old_data))

View File

@ -7,12 +7,12 @@ import os
import sys
# Tool libs
from utils import run, sanitize
import utils
import conf
from objects import GameLanguage
print("=> Phase: languages")
utils.title('LANGUAGES')
if conf.SAVE:
sys.path.append('../../minecraftcodex')
os.environ['DJANGO_SETTINGS_MODULE'] = 'local_settings'
@ -28,9 +28,9 @@ LANGUAGES_STR = []
###
# LOOK FOR CORRECT JAVA FILES
###
print(" => Looking for languages files...")
utils.sub("Looking for languages files:")
directory_list = os.listdir(conf.LANGUAGES_PATH)
print(" Found %d file(s)." % len(directory_list))
utils.echo("found %d file(s)" % len(directory_list), end='\n')
###
# GET LANGUAGES
@ -44,7 +44,7 @@ try:
except:
OLD_LANGUAGES = []
print(" => Mining languages...")
utils.sub('Looking for dataz', end='\n')
for item in directory_list:
if '.lang' in item:
if conf.DEBUG:
@ -103,23 +103,20 @@ if conf.SAVE:
string_obj.save()
print(" => Summary")
# LANGUAGES
[LANGUAGES_STR.append(x.name) for x in LANGUAGES]
new_languages = len(LANGUAGES_STR) - len(OLD_LANGUAGES)
print(" Found %d languages (%d new)." % (len(LANGUAGES_STR), new_languages))
utils.info("Found %d languages (%d new)." % (len(LANGUAGES_STR), new_languages))
if len(LANGUAGES_STR) != len(OLD_LANGUAGES):
print(" Comparision:")
utils.sub('Modifications:', end='\n')
for string in LANGUAGES_STR:
if string not in OLD_LANGUAGES:
print(" + %s" % string)
for lang in LANGUAGES_STR:
if lang not in OLD_LANGUAGES:
utils.sub(' + %s' % lang, end='\n', color=utils.colors.GREEN)
for string in OLD_LANGUAGES:
if string not in LANGUAGES_STR:
print(" - %s" % string)
for lang in OLD_LANGUAGES:
if lang not in LANGUAGES_STR:
utils.sub(' - %s' % lang, end='\n', color=utils.colors.RED)
olditems = open('languages.json', 'w')
olditems.write(json.dumps(LANGUAGES_STR))
@ -127,17 +124,17 @@ olditems.close()
# STRINGS
new_strings = len(STRINGS) - len(OLD_STRINGS)
print(" Found %d strings (%d new) -based on en_US-." % (len(STRINGS), new_strings))
utils.info("Found %d strings (%d new) -based on en_US-." % (len(STRINGS), new_strings))
if len(STRINGS) != len(OLD_STRINGS):
print(" Comparision:")
utils.sub('Modifications:', end='\n')
for string in STRINGS:
if string not in OLD_STRINGS:
print(" + %s" % string)
utils.sub(' + %s' % string, end='\n', color=utils.colors.GREEN)
for string in OLD_STRINGS:
if string not in STRINGS:
print(" - %s" % string)
utils.sub(' - %s' % string, end='\n', color=utils.colors.RED)
olditems = open('strings.json', 'w')
olditems.write(json.dumps(STRINGS))

103
utils/miner/mobs.py Normal file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
# General libs
import re
import json
import os
import sys
# Tool libs
from utils import run, sanitize
import utils
import conf
from objects import GameMob
utils.title('MOS')
if conf.SAVE:
sys.path.append('../../minecraftcodex')
os.environ['DJANGO_SETTINGS_MODULE'] = 'local_settings'
#from database.models import Achievement
###
# GLOBALS
###
ITEMS = []
ITEMS_STR = []
###
# LOOK FOR CORRECT JAVA FILES
###
utils.sub("Looking for java files...")
#utils.sub("Keywords: %s" % ', '.join(conf.ACHIEVEMENTS_JAVA_KEYWORDS))
for keyword in conf.MOBS_JAVA_KEYWORDS:
cmd = run("grep '%s' ./classes/*" % keyword)
for result in cmd:
for line in result.split('\n'):
if line and result is not '':
java_file = os.path.basename(line.strip().split()[0][:-1])
if java_file not in conf.MOBS_FILES:
utils.echo("%s " % java_file, end='')
conf.MOBS_FILES.append(java_file)
utils.echo('\r')
###
# GET ITEMS INFO FROM CLASSFILE
###
utils.sub('Looking for dataz', end='\n')
# TODO OLD Data
for java_file in conf.MOBS_FILES:
print(java_file)
file_handler = open('./classes/%s' % java_file)
data = file_handler.read().split("\n")
item_regex = re.compile(conf.MOBS_PATTERN)
class_error_regex = re.compile('name \'(?P<name>\w+)\' is not defined')
for line in data:
if '"' in line:
t = item_regex.search(line)
if t:
item = t.groupdict()
if conf.DEBUG:
print("Line: " + item['name'])
if item['name'] not in ITEMS_STR:
obj = GameMob(item['name'], item['full'])
ITEMS.append(obj)
ITEMS_STR.append(item['name'])
for x in ITEMS:
print(x)

View File

@ -80,3 +80,44 @@ class GameLanguage(object):
def add_string(self, key, value):
if key not in self.strings:
self.strings[key] = value
###
# ACHIEVEMENTS
###
class GameAchievement(object):
def __init__(self, internal_id, name, *args):
self.id = int(internal_id)
self.name = name
def method(self, *args):
return self
def __getattr__(self, *args):
return self.method
def __str__(self):
return "<Achievement (%d: '%s')>" % (
self.id,
self.name
)
###
# MOBS
###
class GameMob(object):
def __init__(self, name, full, *args):
self.name = name
self.full = full
def method(self, *args):
return self
def __getattr__(self, *args):
return self.method
def __str__(self):
return "<Mob (%s: '%s')>" % (
self.name,
self.full
)

View File

@ -9,3 +9,5 @@ python items.py
python blocks.py
python languages.py
python achievements.py

View File

@ -8,10 +8,11 @@ import json
from PIL import Image
# Tool libs
import utils
import conf
from objects import GameTexture
print("=> Phase: textures")
utils.title("TEXTURES")
if conf.SAVE:
path.append('../../minecraftcodex')
@ -73,21 +74,22 @@ if conf.SAVE:
)
item.save()
print(' => Summary')
# SUMMARY
new_old_data = {}
new_old_data['list'] = []
[new_old_data['list'].append(x.name) for x in TEXTURES]
new_items = len(new_old_data['list'])-len(OLD_TEXTURES['list'])
print(' Fetched %d textures (%d new)' % (len(TEXTURES), new_items))
if new_items > 0:
print(' Modifications:')
utils.info('Found %d textures (%d new)' % (len(TEXTURES), new_items))
if new_items != 0:
utils.sub('Modifications', end='\n')
for item in TEXTURES:
if item.name not in OLD_TEXTURES['list']:
print(' + %s' % item.name)
utils.sub(' + %s' % item.name, end='\n', color=utils.colors.GREEN)
for item in OLD_TEXTURES['list']:
if item not in new_old_data['list']:
print(' - %s' % item)
utils.sub(' - %s' % item, end='\n', color=utils.colors.RED)
olditems = open('textures.json', 'w')
olditems.write(json.dumps(new_old_data))

View File

@ -3,6 +3,8 @@ import re
from conf import CLASS_BLACKLIST
import random
import string
import sys
import os
def replace_classnames(code):
char_set = string.ascii_lowercase
@ -35,3 +37,85 @@ def sanitize(string):
sane = regex.sub(r'\1"\2"', sane)
sane = replace_classnames(sane)
return sane
# Functions
class Colors:
END = '\033[0m'
def __init__(self, c='ter,'):
if c == 'custom':
self.custom()
else:
self.term()
def custom(self):
self.WHITE = '\033[89m'
self.BLACK = '\033[90m'
self.RED = '\033[91m'
self.GREEN = '\033[92m'
self.YELLOW = '\033[93m'
self.BLUE = '\033[94m'
self.PURPLE = '\033[95m'
self.CYAN = '\033[96m'
def term(self):
self.WHITE = '\033[29m'
self.BLACK = '\033[30m'
self.RED = '\033[31m'
self.GREEN = '\033[32m'
self.YELLOW = '\033[33m'
self.BLUE = '\033[34m'
self.PURPLE = '\033[35m'
self.CYAN = '\033[36m'
colors = Colors('custom')
def echo(string, end='\r\n', color=None):
if color:
sys.stdout.write("%s%s%s" % (
color,
string,
colors.END
))
else:
sys.stdout.write(string)
if end:
sys.stdout.write("\n")
sys.stdout.flush()
# Shortcurts
def check_status(status=None, words=['done', 'failed']):
if status is not None:
if status == 0:
echo(words[0], color=colors.GREEN)
else:
echo(words[1], color=colors.RED)
pass
#exit(-1)
def exists(path):
return os.path.exists(path)
def title(string):
print("")
echo("[==] %s" % string, color=colors.PURPLE)
def info(string):
echo("[ i] %s" % string, color=colors.BLUE)
def error(string):
echo("[ E] %s" % string, color=colors.RED)
def success(string):
echo("[OK] %s" % string, color=colors.GREEN)
def sub(string, end='', color=None):
echo(" %s " % string, end=end, color=color)